#pragma once
#include"../LibDGGSKernel/IDGGSKernel.hpp"

_NNU_DGGS_BEGIN
namespace Field {
    template<typename T>
    class DGGSFieldPartition;

    template<typename T>
    class DGGSFieldLayer;

    /// <summary>
    /// 格网数据的field的Band结构定义
    /// </summary>
    template <typename T>
    class DGGSFieldBand
    {
    private:
        friend class DGGSFieldLayer<T>;

        int                     _bandId = 0;                              //波段号
        int                     _level = 0;                               //层级
        DGGSGridType            _gridType = DGGSGridType::Unknown;        //格网类型
        DGGSElementType         _elementType = DGGSElementType::Unknown;  //格网要素类型
        double                  _noDataValue = noData;                 //无效值
        DGGSFieldPartition<T>* _partitions = nullptr;                     //基菱形
        int                     _partitionCount = 0;                      //基菱数

        IDGGSKernel*            _pKernel= nullptr;                        //格网基础操作

        /// <summary>
        /// 按基菱号获取partition
        /// </summary>
        DGGSFieldPartition<T>* getPartitionFromPartIndex(int partitionIndex);

        /// <summary>
        /// 按索引获取partition
        /// </summary>
        bool setPartitionBoxFromIndex(DGGridBBox gridBBox, int index);

        /// <summary>
        /// 按基菱形索引获取partition
        /// </summary>
        bool setPartitionBoxFromPartIndex(DGGridBBox gridBBox, int partitionIndex);

    public:
        DGGSFieldBand();

        DGGSFieldBand(int id, int level, DGGSGridType gridType, DGGSElementType elementType, int partitionCount = baseDiamondCount);

        DGGSFieldBand(const DGGSFieldBand& other);

        ~DGGSFieldBand();

        DGGSGridType getGridType(){return this->_gridType;}

        DGGSElementType getElementType(){return this->_elementType;}

        int getLevel(){return this->_level;}

        /// <summary>
        /// 获取波段号
        /// </summary>
        int getBandID() { return this->_bandId; }

        /// <summary>
        /// 获取无效值
        /// </summary>
        double getNoDataValue() { return this->_noDataValue; }

        /// <summary>
        /// 设置无效值
        /// </summary>
        /// <param name="nodataValue">无效值</param>
        void setNodataValue(double nodataValue);

        /// <summary>
        /// 根据code获取单个属性值
        /// </summary>
        /// <param name="code">code</param>
        T getData(DGGSElementCode code);

        /// <summary>
        /// 根据经纬度获取单个属性值
        /// </summary>
        /// <param name="coord">经纬度</param>
        T getData(GeoCoord coord);

        /// <summary>
        /// 根据code设置单个属性值
        /// </summary>
        /// <param name="code">code</param>
        /// <param name="value">格网值</param>
        bool setData(DGGSElementCode code, T value);

        /// <summary>
        /// 根据经纬度设置单个属性值
        /// </summary>
        /// <param name="coord">经纬度</param>
        /// <param name="value">格网值</param>
        bool setData(GeoCoord coord, T value);

        /// <summary>
        /// code转行列坐标
        /// </summary>
        /// <param name="code">code</param>
        /// <param name="x">列号</param>
        /// <param name="y">行号</param>
        /// <param name="gridBox">包围盒</param>
        bool code2XY(DGGSElementCode code, int& x, int& y, DGGridBBox gridBox);

        /// <summary>
        /// 行列坐标转code
        /// </summary>
        /// <param name="x">列号</param>
        /// <param name="y">行号</param>
        /// <param name="code">code</param>
        /// <param name="gridBox">包围盒</param>
        bool XY2Code(int x, int y, DGGSElementCode& code, DGGridBBox gridBBox);

        /// <summary>
        /// 根据基菱号获取包围盒
        /// </summary>
        /// <param name="partitionId">partitionId</param>
        /// <param name="gridBox">包围盒</param>
        bool getGridBoxFromPartitionIndex(DGGridBBox& gridBBox, int partitionId);

        DGGSFieldBand<T>& operator=(const DGGSFieldBand<T>& other);

        void deepCopy( DGGSFieldBand<T> other);

    };

    template<typename T>
    void DGGSFieldBand<T>::deepCopy( DGGSFieldBand<T> other)
    {
        // 删除当前对象的 _bands 数组
        if(this->_partitions!= nullptr)
            delete[] this->_partitions;
        if(this->_pKernel!= nullptr)
            DestroyKernelObj(this->_pKernel);

        // 复制其他成员变量
        this->_level = other._level;
        this->_bandId = other._bandId;
        this->_partitionCount = other._partitionCount;
        this->_noDataValue = other._noDataValue;
        this->_gridType = other._gridType;
        this->_elementType = other._elementType;
        this->_pKernel= CreateKernelObj(this->_gridType);

        // 分配新的_partitions并进行深度复制
        this->_partitions = new DGGSFieldPartition<T>[this->_partitionCount];
        for (int a = 0; a < this->_partitionCount; a++)
        {
            this->_partitions[a].deepCopy(other._partitions[a]) ;
        }
    }

    template<typename T>
    bool DGGSFieldBand<T>::setPartitionBoxFromPartIndex(DGGridBBox gridBBox, int partitionIndex)
    {
        this->getPartitionFromPartIndex(partitionIndex)->setBBox(gridBBox);
        return true;
    }

    template<typename T>
    bool DGGSFieldBand<T>::getGridBoxFromPartitionIndex(DGGridBBox& gridBBox, int partitionId)
    {
        auto tempPartition = this->getPartitionFromPartIndex(partitionId);
        if (tempPartition != nullptr)
        {
            tempPartition->getBBox(gridBBox);
            return true;
        }
        return false;
    }

    template<typename T>
    DGGSFieldBand<T>::DGGSFieldBand()
    {
        this->_bandId = 0;
        this->_partitions = nullptr;
    }

    template<typename T>
    void DGGSFieldBand<T>::setNodataValue(double nodataValue)
    {
        this->_noDataValue = nodataValue;
        for (int p = 0; p < this->_partitionCount; p++)
            this->_partitions[p].setNoDataValue(nodataValue);
    }

    template<typename T>
    bool DGGSFieldBand<T>::setPartitionBoxFromIndex(DGGridBBox gridBBox, int index)
    {
        if (index < this->_partitionCount)
        {
            this->_partitions[index].setPartitionIndex(gridBBox.basePartition);
            this->_partitions[index].setBBox(gridBBox);
            return true;
        }
        else
            return false;
    }

    template<typename T>
    DGGSFieldPartition<T>* DGGSFieldBand<T>::getPartitionFromPartIndex(int partitionIndex)
    {
        for (int i = 0; i < this->_partitionCount; i++)
        {
            if (this->_partitions[i].getPartitionIndex() == partitionIndex)
                return &this->_partitions[i];
        }
        return nullptr;
    }

    template<typename T>
    DGGSFieldBand<T>::~DGGSFieldBand()
    {
        if(this->_partitions!= nullptr)
            delete[]this->_partitions;
        this->_partitions= nullptr;
        DestroyKernelObj(this->_pKernel);
    }

    template<typename T>
    DGGSFieldBand<T>::DGGSFieldBand(int id, int level, DGGSGridType gridType, DGGSElementType elementType, int partitionCount)
    {
        this->_bandId = id;
        this->_level = level;
        this->_gridType = gridType;
        this->_pKernel= CreateKernelObj(this->_gridType);
        this->_elementType = elementType;
        this->_partitionCount = partitionCount;
        this->_partitions = new DGGSFieldPartition<T>[partitionCount];
        for (int p = 0; p < partitionCount; p++)
        {
            this->_partitions[p] = DGGSFieldPartition<T>(this->_level, this->_gridType);
            if (partitionCount == baseDiamondCount)
                this->_partitions[p].setPartitionIndex(p + 1);
        }
    }

    template<typename T>
    DGGSFieldBand<T>::DGGSFieldBand(const DGGSFieldBand& other)
    {
        this->_gridType = other._gridType;
        if(this->_pKernel!= nullptr)
            DestroyKernelObj(this->_pKernel);
        this->_pKernel= CreateKernelObj(this->_gridType);
        this->_partitionCount = other._partitionCount;
        this->_level = other._level;
        this->_bandId = other._bandId;
        this->_elementType = other._elementType;
        this->_partitions = new DGGSFieldPartition<T>[this->_partitionCount];
        for (int p = 0; p < this->_partitionCount; p++)
            this->_partitions[p] = other._partitions[p];
    }

    template<typename T>
    T DGGSFieldBand<T>::getData(DGGSElementCode code)
    {
        auto partition = this->getPartitionFromPartIndex(code.basePartition);
        if (partition == nullptr)
            return this->_noDataValue;
        else
            return partition->getData(code);
    }

    template<typename T>
    T DGGSFieldBand<T>::getData(GeoCoord coord)
    {
       DGGSElementCode code;
       code.elementType=this->_elementType;
       code.level=this->_level;
       this->_pKernel->encode(coord,code);
       return this->getData(code);
    }

    template<typename T>
    bool DGGSFieldBand<T>::setData(DGGSElementCode code, T value)
    {
        auto partition = this->getPartitionFromPartIndex(code.basePartition);
        if (partition == nullptr)
            return false;
        else
            return partition->setData(code, value);
    }

    template<typename T>
    bool DGGSFieldBand<T>::setData(GeoCoord coord, T value)
    {
        DGGSElementCode code;
        code.elementType=this->_elementType;
        code.level=this->_level;
        this->_pKernel->encode(coord,code);
        return this->setData(code,value);
    }

    template<typename T>
    bool DGGSFieldBand<T>::XY2Code(int x, int y, DGGSElementCode& code, DGGridBBox gridBBox)
    {
        int level = code.level;
        long long int maxRow = gridBBox.rowMin + gridBBox.rowSize + 1;
        //保证格网的编码正确
        if ((static_cast<long long>(y) - x) >= 0 - maxRow && (static_cast<long long>(y) - x) <= maxRow - 1 && (static_cast<long long>(y) + x) >= maxRow - 1 && ((static_cast<long long>(y) + x) <= maxRow * 3 - 2))
        {
            if (x % 2 == 0 && y % 2 == 0)
                x--;
            if (x % 2 != 0 && y % 2 != 0)
                x--;

            int x0 = 0;
            int y0 = (int)maxRow - 1;
            int row = ((y - y0) + (x - x0)) / 2;
            int col = ((x - x0) - (y - y0)) / 2;
            code.row = row;
            code.col = col;
            code.level = level;
            return true;
        }
        else
            return false;
    }

    template<typename T>
    bool DGGSFieldBand<T>::code2XY(DGGSElementCode code, int& x, int& y, DGGridBBox gridBox)
    {
        if (code.basePartition != gridBox.basePartition)
        {
            //表示没找到，可以跳过
            x = -1, y = -1;
            return false;
        }

        long long int colMin = gridBox.colMax - gridBox.colSize + 1;
        long long int rowMin = gridBox.rowMin;
        int x0 = 0;
        int y0 = (int)gridBox.rowSize - 1;

        x = int(code.row - rowMin + code.col - colMin + x0);
        y = int((code.col - colMin) - (code.row - rowMin) + y0);
        if (y < 0 || x < 0)
        {
            //表示没找到，可以跳过
            x = -1, y = -1;
            return false;
        }
        return true;
    }

    template<typename T>
    DGGSFieldBand<T>& DGGSFieldBand<T>::operator=(const DGGSFieldBand<T>& other)
    {
        if (this != &other) {
            // 删除当前对象的 _bands 数组
            delete[] this->_partitions;
            if(this->_pKernel!= nullptr)
                DestroyKernelObj(this->_pKernel);
            // 复制其他成员变量
            this->_level = other._level;
            this->_bandId = other._bandId;
            this->_partitionCount = other._partitionCount;
            this->_noDataValue = other._noDataValue;
            this->_gridType = other._gridType;
            this->_elementType = other._elementType;
            this->_pKernel= CreateKernelObj(this->_gridType);

            // 分配新的_partitions并进行深度复制
            this->_partitions = new DGGSFieldPartition<T>[this->_partitionCount];
            for (int a = 0; a < this->_partitionCount; a++) {
                this->_partitions[a] = other._partitions[a];
            }
        }
        return *this;
    }

}
_NNU_DGGS_END